package stud.mpgame.server;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.mina.core.session.IoSession;

import stud.mpgame.network.Message;
import stud.mpgame.network.NetworkMonitor;
import stud.mpgame.network.TCPNetworkMonitor;
import stud.mpgame.network.util.MessageFilter;
import stud.mpgame.network.IMessageReceivedListener;
import stud.mpgame.network.ISessionListener;

import stud.mpgame.network.service.ping.PingService;

import stud.mpgame.server.game.GameProtocolHandler;
import stud.mpgame.server.game.LoginCommand;
import stud.mpgame.server.game.LoginMessageDescription;

import stud.mpgame.server.test.TestCommand;
import stud.mpgame.server.test.TestCommandDescription;
import stud.mpgame.server.test.TestSnapshot;
import stud.mpgame.server.test.TestSnapshotDescription;

import stud.mpgame.snapshot.SnapshotManager;
import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import stud.mpgame.server.preview.MapInfo;
import stud.mpgame.server.preview.MapInfoMessageDescription;
import stud.mpgame.server.preview.ReadyToGame;
import stud.mpgame.server.preview.ReadyToGameMessageDescription;

public class Main {

    private static final int SNAPSHOT_MANAGER_TICK_TIME = 25;
    private static final int SNAPSHOT_MANAGER_SNAPSHOT_LENGTH = 20;
    private static final int SNAPSHOT_MANAGER_INITIAL_POSITION = 10;
    private static final String defaultConfig = "server/server.properties";

    private final Set<IoSession> readySessions = Collections
            .synchronizedSet(new HashSet< IoSession >());

    private final int protocolVersion = 1;

    private long startAppTime;
    private int delay;

    private NetworkMonitor networkMonitor;
    private SnapshotManager snapshotManager;
    private GameProtocolHandler gameProtocolHandler;

    private PingService pingService; //TODO wrong!
    private boolean need = false; //TODO WRONG!

    private Main() throws Exception {
        startAppTime = System.currentTimeMillis();
        networkMonitor = new TCPNetworkMonitor(startAppTime, protocolVersion);
        snapshotManager = new SnapshotManager(SNAPSHOT_MANAGER_TICK_TIME, SNAPSHOT_MANAGER_SNAPSHOT_LENGTH);
    }

    private void initializationServer(String path) throws Exception {
        ServerConfiguration.getInstance().initialization(path);
        this.delay = ServerConfiguration.getInstance().getDelay();
    }

    public static void main(String[] args) throws Exception {
        Main main = new Main();
        main.initializationServer(args.length > 0 ? args[0] : defaultConfig);
        main.bind();
        main.start();
    }

    private void bind() throws Exception {
    	gameProtocolHandler = new GameProtocolHandler(networkMonitor, snapshotManager);

        MessageFilter messageFilter = new MessageFilter(LoginMessageDescription.getInstance());

        //temporary code start
        int pingDelay = ServerConfiguration.getInstance().getPingDelay();
        pingService = new PingService(networkMonitor, pingDelay, true);
        networkMonitor.addSessionListener(
                new ISessionListener() {
                    public void sessionOpened(IoSession session) {
                        pingService.addSession(session);
                        need = true;
                    }

                    public void sessionClosed(IoSession session) {
                        readySessions.remove(session);
                        pingService.removeSession(session);
                    }
                }
        );
        //Thread.sleep(10000);
        //temporary code end

        networkMonitor.getMessageDispatcher().addMessageReceivedListener(messageFilter,
                new IMessageReceivedListener() {
                    public void messageReceived(Message message) {
                        LoginCommand loginCommand = (LoginCommand) message.getUserObject(LoginCommand.class);
                        loginCommand.setSession(message.getFirstSession());
                        loginCommand.setTime(message.getReceivedTime());
                        System.out.println("LOGIN: " + networkMonitor.nowTime());
                        snapshotManager.addCommand(loginCommand);

                        Message mapInfo = new Message(message.getFirstSession(),MapInfoMessageDescription.getInstance());
						MapInfo map=new MapInfo();
						mapInfo.putUserObject(map);
						mapInfo.send();
                        System.out.println("Map info sent");
                    }
                }
        );

        messageFilter = new MessageFilter(ReadyToGameMessageDescription.getInstance());

        networkMonitor.getMessageDispatcher().addMessageReceivedListener(messageFilter,
                new IMessageReceivedListener() {
                        public void messageReceived(Message message) {
                        ReadyToGame readyToGame = (ReadyToGame) message.getUserObject(ReadyToGame.class);
						readySessions.add(message.getFirstSession());
                        System.out.println("READY");
                        }
                }
        );

        messageFilter = new MessageFilter(TestCommandDescription.getInstance());

        networkMonitor.getMessageDispatcher().addMessageReceivedListener(messageFilter,
                new IMessageReceivedListener() {
                    public void messageReceived(Message message) {
                        TestCommand testCommand = (TestCommand) message.getUserObject(TestCommand.class);
                        System.out.println("CMD: " + testCommand.getTime());
                        snapshotManager.addCommand(testCommand);
                    }
                }
        );

        networkMonitor.bind();
    }

    private void start() throws Exception {

        int time;
        int nextUpdateTime = 0;

        //snapshotManager.setInitialSnapshot(new TestSnapshot(networkMonitor.nowTime(), 1000), SNAPSHOT_MANAGER_INITIAL_POSITION);

        //dummy cycle
        while(true) {
            time = networkMonitor.nowTime();
            if (time >= nextUpdateTime) {
                int dt = time - nextUpdateTime + delay;
                if ( nextUpdateTime == 0 ) {
                    snapshotManager.setInitialSnapshot(new TestSnapshot(0, 1000), SNAPSHOT_MANAGER_INITIAL_POSITION);
                    dt = time;
                }

                snapshotManager.update(dt);
                nextUpdateTime += (dt / delay) * delay;
                sendTestSnapshot();
            } else {
                try {
                    Thread.sleep(nextUpdateTime - time);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    
    private void sendTestSnapshot() throws Exception {
//        if(need) {
//            Thread.sleep(1000);
//            need = false;
//            return;
//        }
        int nowTime = networkMonitor.nowTime();
        for(IoSession session : readySessions) {
            Message message = new Message(session, TestSnapshotDescription.getInstance());

            int ping = pingService.getPing(session);
            if ( ping >= 0 ) {
                TestSnapshot snapshot = (TestSnapshot) snapshotManager.getSnapshot(nowTime + ping);
                //System.out.println(nowTime + "; ping: " + ping + "; " + snapshot.getTime());
                message.putUserObject(snapshot);
                message.send();
            }
        }
    }
}